package Main;


import java.util.ArrayList;

import jgame.JGObject;
import jgame.JGPoint;
import jgame.platform.JGEngine;
import Dragons.Dragon1;
import Dragons.Dragon2;
import Dragons.Dragon3;
import Dragons.Dragon4;
import Dragons.DragonKing;
import Other.Field;
import Robots.Robot1;
import Robots.Robot2;
import Robots.Robot3;
import Robots.Robot4;
import Robots.RobotKing;
import Units.Unit;
import Units.UnitName;

public class Board {
	public static boolean firstToPlay = false; // dragons play first (bottom)
	
	public static int ROBOT1_CID = 100;
	public static int ROBOT2_CID = 200;
	public static int ROBOT3_CID = 300;
	public static int ROBOT4_CID = 400;
	public static int ROBOTKING_CID = 500;
	
	public static int DRAGON1_CID = 600;
	public static int DRAGON2_CID = 700;
	public static int DRAGON3_CID = 800;
	public static int DRAGON4_CID = 900;
	public static int DRAGONKING_CID = 1000;
	
	public static int flyingHeight = 10;
	
	// robots1 starting fields
	public static int robots1SI[] = {3, 2, 3, 2, 3};
	public static int robots1SJ[] = {3, 4, 5, 6, 7};
	
	// robots2 starting fields
	public static int robots2SI[] = {2, 2};
	public static int robots2SJ[] = {2, 8};
	
	// robots3 starting fields
	public static int robots3SI[] = {1, 1};
	public static int robots3SJ[] = {3, 7};
	
	// robots4 starting fields
	public static int robots4SI[] = {0, 0};
	public static int robots4SJ[] = {4, 6};
	
	// robotsKing starting fields
	public static int robotsKingSI[] = {1};
	public static int robotsKingSJ[] = {5};
	
	// dragons1 starting fields
	public static int dragons1SI[] = {11, 12, 11, 12, 11};
	public static int dragons1SJ[] = {3,  4,  5,  6,  7};
	
	// dragons2 starting fields
	public static int dragons2SI[] = {12, 12};
	public static int dragons2SJ[] = {2, 8};

	// dragons3 starting fields
	public static int dragons3SI[] = {13, 13};
	public static int dragons3SJ[] = {3, 7};
	
	// dragons4 starting fields
	public static int dragons4SI[] = {14, 14};
	public static int dragons4SJ[] = {4, 6};
	
	// dragonsKing starting fields
	public static int dragonsKingSI[] = {13};
	public static int dragonsKingSJ[] = {5};
	
	private int boardHeight = 15, boardWidth = 11;
	
	private JGPoint realCoordinates[][] = new JGPoint[boardHeight][boardWidth];
	private UnitName unitName[][] = new UnitName[boardHeight][boardWidth];
	private int cids[][] = new int[boardHeight][boardWidth];
	private boolean allowedFields[][] = new boolean[boardHeight][boardWidth];
	public boolean canAttackFields[][] = new boolean[boardHeight][boardWidth];
	
	// robots
	public Robot1 robots1[] = new Robot1[5];
	public Robot2 robots2[] = new Robot2[2];
	public Robot3 robots3[] = new Robot3[2];
	public Robot4 robots4[] = new Robot4[2];
	public RobotKing robotKing;
	
	// dragons
	public Dragon1 dragons1[] = new Dragon1[5];
	public Dragon2 dragons2[] = new Dragon2[2];
	public Dragon3 dragons3[] = new Dragon3[2];
	public Dragon4 dragons4[] = new Dragon4[2];
	public DragonKing dragonKing;
	
	// gameplay
	public boolean turn;		// false means bottom player's turn, true otherwise
	public boolean selected;	// if unit is selected
	public int selectedUnit;	// ID of selected unit (only if selected is true)
	private boolean animation;
	private int currentStep;
	private boolean killHappenned;
	private boolean gameover;
	private boolean gamewon;	// false, dragons have won, robots otherwise
	
	// fields
	public JGObject[][] moFields = new JGObject[boardHeight][boardWidth];
	public JGObject[][] aFields = new JGObject[boardHeight][boardWidth];
	public JGObject[][] attackFields = new JGObject[boardHeight][boardWidth];
	public JGObject[][] attackClickFields = new JGObject[boardHeight][boardWidth];
	public JGObject[][] selectedFields = new JGObject[boardHeight][boardWidth];
	
	// constructor
	Board(){
		gamePlayInitialization();
		
		for (int i = 0; i < boardHeight; i++){
			for (int j = 0; j < boardWidth; j++){
				if ((i + j) % 2 == 0)
					unitName[i][j] = UnitName.EMPTY;
				else
					unitName[i][j] = UnitName.NULL;
			}
		}
			
		unitName[0][0] = unitName[0][2] = unitName[1][1] = unitName[2][0] = 
			unitName[0][8] = unitName[0][10] = unitName[1][9] = unitName[2][10] = 
				unitName[12][0] = unitName[13][1] = unitName[14][0] = unitName[14][2] =
					unitName[12][10] = unitName[13][9] = unitName[14][8] = unitName[14][10] = UnitName.NULL;
		
		for (int i = 0; i < boardHeight; i++){
			for (int j = 0; j < boardWidth; j++) if ((i + j) % 2 == 0){
				realCoordinates[i][j] = new JGPoint();
				realCoordinates[i][j].x = 54 * j * 3 / 2 + 54 + 21;
				
				realCoordinates[i][j].y = 77 * i / 2 + 43;
			}
		}
		
		createRobots();
		createDragons();
		initializeFields();
	}
	
	private void gamePlayInitialization(){
		selected = false;
		turn = false;
		animation = false;
		gameover = false;
		gamewon = false;
		killHappenned = false;
	}
	
	private void initializeFields(){
		for (int i = 0; i < boardHeight; i++){
			for (int j = 0; j < boardWidth; j++){
				if (!inBoard(i, j)) continue;
				attackFields[i][j] = new JGObject("dmofields2", true, -1000, -100, 1, "field_attack");
				attackClickFields[i][j] = new JGObject("dmofields1", true, -1000, -100, 1, "field_attack_click");
				aFields[i][j] = new JGObject("amofields", true, -1000, -100, 1, "field_move");
				moFields[i][j] = new JGObject("cmofields", true, -1000, -100, 1, "field_mouseover");
				selectedFields[i][j] = new JGObject("bmofields", true, -1000, -100, 1, "field_selected");
			}
		}
	}
	
	private void resetAttackFields(){		
		JGPoint m = new JGPoint(Game.game.getMouseX(), Game.game.getMouseY());
		Field f = getField(m.x, m.y);
		
		/*Field attacked[] = new Field[5];
		
		if (selected) attacked = (Field[]) getSelectedUnit().canAttack(f).toArray();
		else {
			attacked = new Field[0];
		}*/
		
		ArrayList<Field> attacked = new ArrayList<Field>();
		attacked.clear();
		if (selected) {
			attacked = getSelectedUnit().whereAttack(f);
		}
		
		for (int i = 0; i < boardHeight; i++){
			for (int j = 0; j < boardWidth; j++){
				if (!inBoard(i, j)) continue;
				
				Field p1 = new Field(i,j);
				
				//boolean att = true;
				//if (i!=4 || j!=0) att = false;
				boolean att = false;
				for(int l = 0; l<attacked.size(); l++) {
					if (p1.compare(attacked.get(l))) att = true; 
				}
		
				if (att && selected){
					JGPoint p = getRealCoordinates(i, j);
					attackFields[i][j].setPos(p.x - 55, p.y - 77/2);
				}
				else {
					attackFields[i][j].setPos(-1000, 1);
				}
			}
		}
	}
	
	private void resetMoveFields(){
		for (int i = 0; i < boardHeight; i++){
			for (int j = 0; j < boardWidth; j++){
				if (!inBoard(i, j)) continue;
		
				if (isAllowed(i, j)){
					JGPoint p = getRealCoordinates(i, j);
					aFields[i][j].setPos(p.x - 55, p.y - 77/2);
				}
				else {
					aFields[i][j].setPos(-1000, 1);
				}
			}
		}
	}
	
	private void resetCanAttackFields(){
		for (int i = 0; i < boardHeight; i++){
			for (int j = 0; j < boardWidth; j++){
				if (!inBoard(i, j)) continue;
		
				if (canAttackFields[i][j]){
					JGPoint p = getRealCoordinates(i, j);
					attackClickFields[i][j].setPos(p.x - 55, p.y - 77/2);
				}
				else {
					attackClickFields[i][j].setPos(-1000, 1);
				}
			}
		}
	}
	
	private void resetMouseOverFields(){
		JGPoint m = new JGPoint(Game.game.getMouseX(), Game.game.getMouseY());
		Field f = getField(m.x, m.y);
		
		for (int i = 0; i < boardHeight; i++){
			for (int j = 0; j < boardWidth; j++){
				if (!inBoard(i, j)) continue;
				if (i == f.i && j == f.j && selected){
					JGPoint p = getRealCoordinates(i, j);
					moFields[i][j].setPos(p.x - 55, p.y - 77/2);
				}
				else {
					moFields[i][j].setPos(-1000, 1);
				}
			}
		}
	}
	
	private void resetSelectedFields(){
		for (int i = 0; i < boardHeight; i++){
			for (int j = 0; j < boardWidth; j++){
				if (!inBoard(i, j)) continue;
				
				if (cids[i][j] == getSelectedUnitID() && selected){
					JGPoint p = getRealCoordinates(i, j);
					selectedFields[i][j].setPos(p.x - 55, p.y - 77/2);
				}
				else {
					selectedFields[i][j].setPos(-1000, 1);
				}
			}
		}
	}
	
	public void resetFieldsView(){
		resetMoveFields();		
		resetCanAttackFields();
		resetMouseOverFields();
		resetAttackFields();
		resetSelectedFields();
	}
	
	private void createRobots(){
		// create robots1
		for (int i = 0; i < 5; i++){
			cids[robots1SI[i]][robots1SJ[i]] = ROBOT1_CID + i;
			unitName[robots1SI[i]][robots1SJ[i]] = UnitName.ROBOT1;
			
			JGPoint p = getRealCoordinates(robots1SI[i], robots1SJ[i]);
			robots1[i] = new Robot1("robot1", true, p.x - robots1[i].imageWidth/2, p.y - Robot1.imageHeight/2, ROBOT1_CID + i);
			robots1[i].setField(new Field(robots1SI[i], robots1SJ[i]));
		}
		
		// create robots2
		for (int i = 0; i < 2; i++){
			cids[robots2SI[i]][robots2SJ[i]] = ROBOT2_CID + i;
			unitName[robots2SI[i]][robots2SJ[i]] = UnitName.ROBOT2;
			
			JGPoint p = getRealCoordinates(robots2SI[i], robots2SJ[i]);
			robots2[i] = new Robot2("robot2", true, p.x - robots2[i].imageWidth/2, p.y - robots2[i].imageHeight/2, ROBOT2_CID + i);
			robots2[i].setField(new Field(robots2SI[i], robots2SJ[i]));
		}
		
		// create robots3
		for (int i = 0; i < 2; i++){
			cids[robots3SI[i]][robots3SJ[i]] = ROBOT3_CID + i;
			unitName[robots3SI[i]][robots3SJ[i]] = UnitName.ROBOT3;
			
			JGPoint p = getRealCoordinates(robots3SI[i], robots3SJ[i]);
			robots3[i] = new Robot3("robot3", true, p.x - robots3[i].imageWidth/2, p.y - robots3[i].imageHeight/2, ROBOT3_CID + i);
			robots3[i].setField(new Field(robots3SI[i], robots3SJ[i]));
		}
		
		// create robots4
		for (int i = 0; i < 2; i++){
			cids[robots4SI[i]][robots4SJ[i]] = ROBOT4_CID + i;
			unitName[robots4SI[i]][robots4SJ[i]] = UnitName.ROBOT4;
			
			JGPoint p = getRealCoordinates(robots4SI[i], robots4SJ[i]);
			robots4[i] = new Robot4("robot4", true, p.x - robots4[i].imageWidth/2, p.y - robots4[i].imageHeight/2, ROBOT4_CID + i);
			robots4[i].setField(new Field(robots4SI[i], robots4SJ[i]));
		}
		
		// create robotKing
		cids[robotsKingSI[0]][robotsKingSJ[0]] = ROBOTKING_CID;
		unitName[robotsKingSI[0]][robotsKingSJ[0]] = UnitName.ROBOTKING;
		
		JGPoint p = getRealCoordinates(robotsKingSI[0], robotsKingSJ[0]);
		robotKing = new RobotKing("robotKing", true, p.x - robotKing.imageWidth/2, p.y - robotKing.imageHeight/2, ROBOTKING_CID);
		robotKing.setField(new Field(robotsKingSI[0], robotsKingSJ[0]));
	}
	
	public void createDragons(){
		// create dragons1
		for (int i = 0; i < 5; i++){
			cids[dragons1SI[i]][dragons1SJ[i]] = DRAGON1_CID + i;
			unitName[dragons1SI[i]][dragons1SJ[i]] = UnitName.DRAGON1;
			
			JGPoint p = getRealCoordinates(dragons1SI[i], dragons1SJ[i]);
			dragons1[i] = new Dragon1("dragon1", true, p.x - dragons1[i].imageWidth/2, p.y - Robot1.imageHeight/2, DRAGON1_CID + i);
			dragons1[i].setField(new Field(dragons1SI[i], dragons1SJ[i]));
		}
		
		// create dragons2
		for (int i = 0; i < 2; i++){
			cids[dragons2SI[i]][dragons2SJ[i]] = DRAGON2_CID + i;
			unitName[dragons2SI[i]][dragons2SJ[i]] = UnitName.DRAGON2;
			
			JGPoint p = getRealCoordinates(dragons2SI[i], dragons2SJ[i]);
			dragons2[i] = new Dragon2("dragon2", true, p.x - dragons2[i].imageWidth/2, p.y - dragons2[i].imageHeight/2 - flyingHeight, DRAGON2_CID + i);
			dragons2[i].setField(new Field(dragons2SI[i], dragons2SJ[i]));
		}
		
		// create dragons3
		for (int i = 0; i < 2; i++){
			cids[dragons3SI[i]][dragons3SJ[i]] = DRAGON3_CID + i;
			unitName[dragons3SI[i]][dragons3SJ[i]] = UnitName.DRAGON3;
			
			JGPoint p = getRealCoordinates(dragons3SI[i], dragons3SJ[i]);
			dragons3[i] = new Dragon3("dragon3", true, p.x - dragons3[i].imageWidth/2, p.y - dragons3[i].imageHeight/2, DRAGON3_CID + i);
			dragons3[i].setField(new Field(dragons3SI[i], dragons3SJ[i]));
		}
		
		// create dragons4
		for (int i = 0; i < 2; i++){
			cids[dragons4SI[i]][dragons4SJ[i]] = DRAGON4_CID + i;
			unitName[dragons4SI[i]][dragons4SJ[i]] = UnitName.DRAGON4;
			
			JGPoint p = getRealCoordinates(dragons4SI[i], dragons4SJ[i]);
			dragons4[i] = new Dragon4("dragon4", true, p.x - dragons4[i].imageWidth/2, p.y - dragons4[i].imageHeight/2, DRAGON4_CID + i);
			dragons4[i].setField(new Field(dragons4SI[i], dragons4SJ[i]));
		}
		
		// create dragonKing
		cids[dragonsKingSI[0]][dragonsKingSJ[0]] = DRAGONKING_CID;
		unitName[dragonsKingSI[0]][dragonsKingSJ[0]] = UnitName.DRAGONKING;
		
		JGPoint p = getRealCoordinates(dragonsKingSI[0], dragonsKingSJ[0]);
		dragonKing = new DragonKing("dragonKing", true, p.x - dragonKing.imageWidth/2, p.y - dragonKing.imageHeight/2, DRAGONKING_CID);
		dragonKing.setField(new Field(dragonsKingSI[0], dragonsKingSJ[0]));
	}
	
	// methods
	
	public boolean getAnimation(){ return animation; }
	public void setAnimation(boolean animation){ this.animation = animation; }
	
	public int getSelectedUnitID(){ return selectedUnit; }
	public Unit getSelectedUnit(){
		// check if robot1 or dragon1

		for (int i = 0; i < 5; i++){
			if (getSelectedUnitID() == robots1[i].getCID()){
				return robots1[i];
			}
			else if (getSelectedUnitID() == dragons1[i].getCID()){
				return dragons1[i];
			}
		}
		
		// check if robot2 or dragon2
		for (int i = 0; i < 2; i++){
			if (getSelectedUnitID() == robots2[i].getCID()){
				return robots2[i];
			}
			else if (getSelectedUnitID() == dragons2[i].getCID()){
				return dragons2[i];
			}
		}
		
		// check if robot3 or dragon3
		for (int i = 0; i < 2; i++){
			if (getSelectedUnitID() == robots3[i].getCID()){
				return robots3[i];
			}
			else if (getSelectedUnitID() == dragons3[i].getCID()){
				return dragons3[i];
			}
		}
		
		// check if robot4 or dragon4
		for (int i = 0; i < 2; i++){
			if (getSelectedUnitID() == robots4[i].getCID()){
				return robots4[i];
			}
			else if (getSelectedUnitID() == dragons4[i].getCID()){
				return dragons4[i];
			}
		}
		
		// check if robotKing or dragonKing
		if (getSelectedUnitID() == robotKing.getCID()){
			return robotKing;
		}
		else if (getSelectedUnitID() == dragonKing.getCID()){
			return dragonKing;
		}
		
		return null;
	}
	
	public boolean getTurn(){ return turn; }
	public int getCID(int i, int j){ return cids[i][j]; }
	public boolean isAllowed(int i, int j){ return allowedFields[i][j]; }
	
	// reset methods
	public void resetAllowed(){
		for (int i = 0; i < boardHeight; i++){
			for (int j = 0; j < boardWidth; j++){
				allowedFields[i][j] = false;
				canAttackFields[i][j] = false;
			}
		}
	}
	
	// set 
	public void setAllowed(Unit unit){
		for (int i = 0; i < boardHeight; i++){
			for (int j = 0; j < boardWidth; j++){
				if (unit.canMove(new Field(i, j))) allowedFields[i][j] = true;
				else allowedFields[i][j] = false;
				
				if (unit.canAttack(new Field(i, j))) canAttackFields[i][j] = true;
				else canAttackFields[i][j] = false;
			}
		}
		selectedUnit = unit.getCID();
	}
	
	public void setCID(int i, int j, int cid){ this.cids[i][j] = cid; }
	
	
	
	public static double distance(JGPoint a, JGPoint b){
		return Math.sqrt((a.x-b.x) * (a.x-b.x) + (a.y-b.y) * (a.y-b.y));
	}
	
	public Field getField(int x, int y){
		Field ret = new Field(0, 0);
		
		for (int i = 0; i < boardHeight; i++)
			for (int j = 0; j < boardWidth; j++) if (inBoard(i, j))
				if (distance(realCoordinates[ret.i][ret.j], new JGPoint(x, y)) > distance(realCoordinates[i][j], new JGPoint(x, y))){
					ret.i = i; ret.j = j;
				}
				
		return ret;
	}
	
	public JGPoint getRealCoordinates(int i, int j){
		return realCoordinates[i][j];
	}
	
	public JGPoint getRealCoordinates(Field f){
		return new JGPoint(this.getRealCoordinates(f.i, f.j));
	}
	
	public int getHeight(){
		return boardHeight;
	}
	
	public int getWidth(){
		return boardWidth;
	}

	public UnitName getUnitName(int i, int j){
		return unitName[i][j];
	}
	
	public void gameOver(){ gameover = true; }
	public boolean isGameOver(){ return gameover; }
	
	public boolean dragonsWon(){ return !gamewon; }
	public void setDragonsWon(){ gamewon = false; }
	public void setRobotsWon(){ gamewon = true; }
	
	public int getCurrentStep(){ return currentStep; }
	public void setCurrentStep(int step){ currentStep = step; }
	public void resetCurrentStep(){ currentStep = 0; }
	public void increaseCurrentStep(){ currentStep++; }
	
	public boolean killHappenned(){ return killHappenned; }
	public void setKillHappenned(boolean kill){ killHappenned = kill; }
	
	public boolean inBoard(int i, int j){
		if (i < 0 || j < 0 || i >= boardHeight || j >= boardWidth) return false;
		return unitName[i][j] != UnitName.NULL;
	}
	
	public void playSound(){
		for (int i = 0; i < 5; i++){
			if (robots1[i].moveSound){
				Game.game.playAudio("robot1soundmove");
				robots1[i].moveSound = false;
			}
		}
	}

	public void moveFromTo(Field f, Field t) {
		
		int killedCID = getCID(t.i, t.j);
		if (killedCID != 0){
			// if field t is unit (then that unit is enemy unit and it's killed)
			setKillHappenned(true);
			Unit.getUnitFromCID(getCID(t.i, t.j)).kill();
			
			if (killedCID == DRAGONKING_CID){
				setRobotsWon();
				gameOver();
			}
			else if (killedCID == ROBOTKING_CID){
				setDragonsWon();
				gameOver();
			}
		}
		
		setCID(t.i, t.j, getCID(f.i, f.j));
		setCID(f.i, f.j, 0);
		
		unitName[t.i][t.j] = unitName[f.i][f.j];
		unitName[f.i][f.j] = UnitName.EMPTY;
		
		// if steps == 0 change turn
		if (getSelectedUnit().getSteps() == 0 || killHappenned()){
			changeTurn();
		}
	}
	
	public void changeTurn() {
		this.selected = false;
		this.turn = !this.turn;
		getSelectedUnit().resetSteps();
		resetCurrentStep();
		resetAllowed();
		setKillHappenned(false);
	}

	public boolean isDragon(int cid){
		return cid >= DRAGON1_CID && cid <= DRAGONKING_CID;
	}
	
	public boolean isRobot(int cid){
		return cid >= ROBOT1_CID && cid <= ROBOTKING_CID;
	}
}
